package com.example.exception

import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay
import kotlinx.coroutines.job
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield
import org.junit.Test
import java.lang.ArithmeticException
import java.lang.IllegalArgumentException
import java.util.ServiceLoader
import java.util.concurrent.CancellationException

class CoroutineTest01 {


    @Test
    fun `test CoroutineContext`() = runBlocking<Unit> {//主线程变成协程
        launch(Dispatchers.Default) {
//            current DefaultDispatcher-worker-1 @coroutine#2
            println("current ${Thread.currentThread().name}")
        }
        launch(Dispatchers.Default + CoroutineName("test")) {
//            current DefaultDispatcher-worker-1 @test#3
            println("current ${Thread.currentThread().name}")
        }
    }


    @Test
    fun `test CoroutineContext extend`() = runBlocking<Unit> {
        val scope = CoroutineScope(Job() + Dispatchers.IO + CoroutineName("test"))
        val job = scope.launch {
//            新的协程："test#2":StandaloneCoroutine{Active}@1a212b2b DefaultDispatcher-worker-1 @test#2
            println("新的协程：${coroutineContext[Job]} ${Thread.currentThread().name}")
            val result = async {
//                新的协程1："test#3":DeferredCoroutine{Active}@3c68f2a5 DefaultDispatcher-worker-3 @test#3
                println("新的协程1：${coroutineContext[Job]} ${Thread.currentThread().name}")
            }.await()
            println("result=$result")
        }
        job.join()
    }

    @Test
    fun `test CoroutineContext extend2`() = runBlocking<Unit> {
        val coroutineexceptionhandler = CoroutineExceptionHandler { _, throwex ->
//            throwex=java.lang.IllegalArgumentException: 错误了
            println("throwex=$throwex")
        }
        val scope = CoroutineScope(Job() + Dispatchers.Main + coroutineexceptionhandler)
        val job = scope.launch(Dispatchers.IO) {
            //新协程:调度器是IO
//            协程：DefaultDispatcher-worker-1 @coroutine#2
            println("协程：${Thread.currentThread().name}")
            throw IllegalArgumentException("错误了")
        }
        job.join()
    }

    @Test
    fun `test exception propagation_custom`() = runBlocking<Unit> {
        val handlers: List<CoroutineExceptionHandler> = ServiceLoader.load(
            CoroutineExceptionHandler::class.java,
            CoroutineExceptionHandler::class.java.classLoader
        ).iterator().asSequence().toList()
        println(handlers.get(0))
        println(handlers.get(1))
    }

    @Test
    fun `test exception propagation`() = runBlocking<Unit> {
        val job = GlobalScope.launch {
            throw IllegalArgumentException("错误了")//这里捕捉异常
        }
        job.join()
        val deferred = GlobalScope.async {
            throw ArithmeticException("错误了")//这里没有抛出异常，在await调用时抛出
        }
        deferred.await()//只有捕捉这里的才是正在异常抛出的地方
    }


    @Test
    fun `test exception propagation2`() = runBlocking<Unit> {
        val scope = CoroutineScope(Job())
        val job = scope.launch {
            async {//其他协程创建的协程，产生的异常可以抛出
                try {
                    throw ArithmeticException("错误了")
                } catch (e: Exception) {
                    println("哈哈哈哈哈")
                }
            }
        }
        job.join()
        //异常的传播特性
        //首先它会取消所有的子级，然后取消自己，接着继续上报给父级
    }

    @Test
    fun `test supervisorJob`() = runBlocking<Unit> {
        //SupervisorJob 使得子协程不会干扰其他的协程，异常时其他协程依然可以执行
        val superVisorJob = CoroutineScope(SupervisorJob())
        val job1 = superVisorJob.launch {
            delay(100)
            println("job1")
            throw ArithmeticException("错误了")
        }
        val job2 = superVisorJob.launch {
            try {
                delay(1000)
                println("job2")
            } finally {
                println("依然执行，释放资源")
            }
        }
        joinAll(job1, job2)
    }

    @Test
    fun `test supervisorJob2`() = runBlocking<Unit> {
        //SupervisorJob 使得子协程不会干扰其他的协程，异常时其他协程依然可以执行
        val superVisorJob = CoroutineScope(SupervisorJob())
        val job1 = superVisorJob.launch {
            delay(100)
            println("job1")
            throw ArithmeticException("错误了")
        }
        val job2 = superVisorJob.launch {
            try {
                delay(Long.MAX_VALUE)
                println("job2")
            } finally {
                println("依然执行，释放资源")
            }
        }
        delay(200)
        superVisorJob.cancel()//模拟当View从界面上移除，所有子协程取消掉
        joinAll(job1, job2)
    }

    @Test
    fun `test SupervisorScope`() = runBlocking<Unit> {
        //child1 失败 child 2继续执行
        supervisorScope {
            launch {
                delay(100)
                println("child 1")
                throw ArithmeticException("错误了")
            }
            try {
                delay(Long.MAX_VALUE)
            } finally {
                println("child 2 finish ")
            }
        }
    }

    @Test
    fun `test SupervisorScope2`() = runBlocking<Unit> {
        try {
            supervisorScope {
                //            在supervisorScope作用域里执行代码出错时，它的子协程会被取消掉
                launch {
                    try {
                        delay(2200)
                        println("child 1")
                    } finally {
                        println("child 1 被取消了")
                    }
                }
                yield()
                println("supervisorScope抛出异常，在作用域")
                throw ArithmeticException("错误了")
            }
        } catch (e: Exception) {
            println("外部的异常捕获")
        }
    }

    @Test
    fun `test CoroutineExceptionHandler`() = runBlocking<Unit> {
        //只有在根协程和launch主动抛出的异常才能被捕获，async是无法捕获的
        val handler = CoroutineExceptionHandler { _, throwex ->
//            throwex=java.lang.IllegalArgumentException: 错误了
            println("throwex=$throwex")
        }
        val job = GlobalScope.launch(handler) {
            throw ArithmeticException("launch错误了")
        }
        val deferred = GlobalScope.async(handler) {
            throw ArithmeticException("async错误了")
        }
        job.join()
        deferred.await()
    }

    @Test
    fun `test CoroutineExceptionHandler2`() = runBlocking<Unit> {
        //只有在根协程和launch主动抛出的异常才能被捕获，async是无法捕获的
        val handler = CoroutineExceptionHandler { _, throwex ->
//            throwex=java.lang.IllegalArgumentException: 错误了
            println("throwex=$throwex")
        }
        val scope = CoroutineScope(Job())
        val job = scope.launch(handler) {
//            throw ArithmeticException("launch错误了")
            launch { throw ArithmeticException("launch错误了") }
        }
        job.join()
    }

    @Test
    fun `test CoroutineExceptionHandler3`() = runBlocking<Unit> {
        //只有在根协程和launch主动抛出的异常才能被捕获，async是无法捕获的
        val handler = CoroutineExceptionHandler { _, throwex ->
//            throwex=java.lang.IllegalArgumentException: 错误了
            println("throwex=$throwex")
        }
        val scope = CoroutineScope(Job())
        val job = scope.launch {
            //把handler交给子协程的子协程，并没有继承顶级上下文，所以无法捕获异常
            launch(handler) { throw ArithmeticException("launch错误了") }
        }
        job.join()
    }

    @Test
    fun `test cancel and exception`() = runBlocking<Unit> {
        //子协程取消并不会把父协程给取消掉，发生异常才会往上级传播
        val job = launch {
            val child = launch {
                try {
                    delay(Long.MAX_VALUE)
                } catch (e: Exception) {
                    e.printStackTrace()
                } finally {
                    println("Child is cancelled")
                }
            }
            yield()
            println("cancelling child")
            child.cancelAndJoin()
            yield()
            println("Parent is not cancelled")
        }
        job.join()
    }

    @Test
    fun `test cancel and exception2`() = runBlocking<Unit> {
        // 如果一个协程遇到出了被主动取消导致的异常，那么它将使用改异常去取消父协程，当所有子协程都执行完后，父类异常才被执行
        val handler = CoroutineExceptionHandler { _, exception ->
            println("父类异常获取：$exception")
        }
        val job = GlobalScope.launch(handler) {
            val job1 = launch {
                try {
                    println("job1 run work")
                    delay(Long.MAX_VALUE)
                } finally {
                    withContext(NonCancellable) {//因为delay(Long.MAX_VALUE)是挂起函数，一直延迟执行，所以加这个
                        println("job1 cancel 11")
                        delay(100)
                        println("job2 cancel 22")
                    }
                }
            }
            val job2 = launch {
                delay(10)
                println("job2 抛出异常，job1 将被取消")
                throw ArithmeticException()
            }
        }
        job.join()
    }

    @Test
    fun `test exception aggregation`() = runBlocking<Unit> {
        var forName = Class.forName("com.example.exception.CoroutineTest01")
//        var forName = Class.forName("com.example.exception.H")
        val `object` = forName.newInstance()
        val annotations: Array<Annotation> = `object`.javaClass.getAnnotations()
        if (annotations != null) {
            if (annotations.isEmpty()) {
                println("不是kotlin list isEmpty-----")
            } else {
                println("不是kotlin list -----"+annotations.size)
                for (i in annotations.indices) {
                    if (annotations[i].toString().contains("kotlin")) {
                        println("是kotlin"+annotations[i].annotationClass)
                        break
                    } else {
                        println("是kotlin-----" + annotations[i].toString())
                    }
                }
            }
        } else {
            println("不是kotlin-----")
        }
        //演示聚合异常的捕获
        val handler = CoroutineExceptionHandler { _, exception ->
            println("父类异常获取：$exception----${exception.suppressed.contentToString()}")
        }
        val job = GlobalScope.launch(handler) {
            val job1 = launch {
                try {
                    delay(Long.MAX_VALUE)
                } finally {
                    throw ArithmeticException()
                }

            }
            val job2 = launch {
                delay(100)
                throw ArithmeticException()
            }
        }
        job.join()
    }
}